#include "ThreadPoolSchedule.h"
#include "Thread.h"

#include "Task.h"



CThreadPoolSchedule::CThreadPoolSchedule(uint64_t InWorkerNum)
{
    Threads.reserve(InWorkerNum);

    if(InWorkerNum<=0)
    {
        WorkingThreadNum=std::thread::hardware_concurrency()-1;
        if(WorkingThreadNum==0)
        {
            WorkingThreadNum=1;
        }
    }
    else
    {
        WorkingThreadNum=InWorkerNum;
    }

    //reserve is necessary , prevent reallocate and data race on vector
    Threads.reserve(WorkingThreadNum);

    for(uint64_t WorkerIndex=0; WorkerIndex<WorkingThreadNum;WorkerIndex++)
    {
        CThread* NewThread= new CThread();
        Threads.push_back(NewThread);
        NewThread->LowLevel=std::thread(&CThreadPoolSchedule::FindAndRunWork,this,WorkerIndex);
    }
}

CThreadPoolSchedule::~CThreadPoolSchedule()
{
    for(auto Thread: Threads)
    {
        delete Thread;
    }
    Threads.clear();
    
}

void CThreadPoolSchedule::AddTask(TThreadSafeSharedPtr<CTaskBase> InTask)
{
    InTask->State.store(CTaskBase::EState::WaitingInSchedule);
    {
        std::lock_guard<std::mutex> LockGuard(WaitingTasksLocker);
        WaitingTasks.push(InTask);
    }
}

bool CThreadPoolSchedule::HaveWaitingTask()
{
    std::lock_guard<std::mutex> LockGuard(WaitingTasksLocker);
    return !WaitingTasks.empty();
}

void CThreadPoolSchedule::Shutdown()
{

    for(auto Thread: Threads)
    {
        Thread->bIsShutdown.store(true);
    }

    while(true)
    {
        if(WorkingThreadNum.load()==0)
        {
            break;
        }
        else
        {
            //wait working thread finish
            std::this_thread::yield();
        }
    }

    while(!WaitingTasks.empty())
    {
        WaitingTasks.pop();
    }

    
}

void CThreadPoolSchedule::FindAndRunWork(uint64_t ThreadIndex)
{
    while(true)
    {
        //check if need shutdown
        if(Threads[ThreadIndex]->bIsShutdown.load())
        {
            WorkingThreadNum.fetch_sub(1);
            break;
        }


        {
            WaitingTasksLocker.lock();
            if(WaitingTasks.empty())
            {
                WaitingTasksLocker.unlock();

                //do not have work, just sleep
                std::this_thread::yield();
                continue ;
            }
            else
            {
                auto Task = WaitingTasks.front();
                WaitingTasks.pop();
                WaitingTasksLocker.unlock();
            
                Task->State.store(CTaskBase::EState::Executing);
                bool bSucceed= Task->Do();
                if(bSucceed)
                {
                    if(Task->NextTask)
                    {
                        auto NextTask=Task->NextTask;
                        Task->NextTask=nullptr; // clear the next task, prevent circular reference
                        NextTask->AddToSchedule();
                    }
                }
                else
                {
                    if(Task->FailedTask)
                    {
                        auto FailedTask=Task->FailedTask;
                        Task->FailedTask=nullptr; // clear the next task, prevent circular reference
                        FailedTask->AddToSchedule();
                    }
                }
                continue;
            }
        }
    
    }
}
